package com.atguigu.gulimall.ware.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.atguigu.common.exception.NoWareStockException;
import com.atguigu.common.exception.SkuNoStockException;
import com.atguigu.common.to.OrderTo;
import com.atguigu.common.to.StockLockedTo;
import com.atguigu.common.utils.R;
import com.atguigu.gulimall.product.entity.SkuInfoEntity;
import com.atguigu.gulimall.ware.dao.WareOrderTaskDetailDao;
import com.atguigu.gulimall.ware.entity.WareOrderTaskDetailEntity;
import com.atguigu.gulimall.ware.entity.WareOrderTaskEntity;
import com.atguigu.gulimall.ware.exception.OrderFeignException;
import com.atguigu.gulimall.ware.feign.OrderFeignService;
import com.atguigu.gulimall.ware.feign.ProductFeignService;
import com.atguigu.gulimall.ware.service.WareOrderTaskDetailService;
import com.atguigu.gulimall.ware.service.WareOrderTaskService;
import com.atguigu.gulimall.ware.service.WareSkuService;
import com.atguigu.gulimall.ware.vo.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.atguigu.common.utils.PageUtils;
import com.atguigu.common.utils.Query;

import com.atguigu.gulimall.ware.dao.WareSkuDao;
import com.atguigu.gulimall.ware.entity.WareSkuEntity;
import org.springframework.transaction.annotation.Transactional;


@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService {

    @Autowired
    OrderFeignService orderFeignService;

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Autowired
    WareOrderTaskDetailService wareOrderTaskDetailService;

    @Autowired
    WareOrderTaskService wareOrderTaskService;

    @Autowired
    ProductFeignService productFeignService;

    @Autowired
    WareSkuDao wareSkuDao;

    @Autowired
    WareOrderTaskDetailDao wareOrderTaskDetailDao;


    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        /**
         *    wareId: 123,//仓库id
         *    skuId: 123//商品id
         */
        QueryWrapper<WareSkuEntity> queryWrapper = new QueryWrapper<>();
        String wareId = (String) params.get("wareId");
        if (!StringUtils.isEmpty(wareId)) {
            queryWrapper.eq("ware_id", wareId);
        }
        String skuId = (String) params.get("skuId");
        if (!StringUtils.isEmpty(skuId)) {
            queryWrapper.eq("sku_id", wareId);
        }
        IPage<WareSkuEntity> page = this.page(
                new Query<WareSkuEntity>().getPage(params),
                queryWrapper
        );

        return new PageUtils(page);
    }

    @Override
    public void addStock(Long skuId, Long wareId, Integer skuNum) throws Exception {
        List<WareSkuEntity> wareSkuEntities = wareSkuDao.selectList(new QueryWrapper<WareSkuEntity>().eq("sku_id", skuId).eq("ware_id", wareId));
        if (wareSkuEntities == null || wareSkuEntities.size() == 0) {
            WareSkuEntity wareSkuEntity = new WareSkuEntity();
            wareSkuEntity.setSkuId(skuId);
            wareSkuEntity.setWareId(wareId);
            wareSkuEntity.setStock(skuNum);
            //锁定库存
            wareSkuEntity.setStockLocked(0);
            //远程查询商品名称
            //TODO 1.自己处理异常，查询名称错误不影响整个入库操作
            // 2.向上抛出异常，外层接收异常并回滚
            // 3.还有什么方法出现异常不回滚
            try {
                R r = productFeignService.info(skuId);
                if (r.getCode() == 0){
                    //LinkedHashMap无法转换实体类,类型转换异常
                    /**
                     * 远程传参和接收参数时一定要指定泛型！！！！！
                     *
                     * 因为rpc远程调用在底层还是使用的HTTPClient
                     * 所以在传递参数的时候，必定要有个顺序，当你传递map的时候map里面的值也要有顺序，
                     * 不然服务层在接的时候就出问题了，所以它才会从map转为linkedhashMap
                     * ！spring 有一个类叫ModelMap，继承了linkedhashMap public class ModelMap extends LinkedHashMap ,
                     * 所以一个接口返回的结果就可以直接用ModelMap来接，注意ModelMap是没有泛型的，不管你返回的结果是什么类型的map，
                     * 泛型是多复杂的map，都可以直接new一个Modelmap，用它来接返回的结果
                     */
//                    Map<String,Object> map = (Map<String, Object>) r.get("skuInfo");
//                    String skuName = (String) map.get("skuName");
//                    wareSkuEntity.setSkuName(skuName);

                    Object obj = r.get("skuInfo");
                    ObjectMapper objectMapper = new ObjectMapper();
                    SkuInfoEntity skuInfoEntity = objectMapper.convertValue(obj, SkuInfoEntity.class);
                    wareSkuEntity.setSkuName(skuInfoEntity.getSkuName());
                }
            } catch (Exception e) {
                e.printStackTrace();
                throw new Exception();
            }
            wareSkuDao.insert(wareSkuEntity);
        } else {
            wareSkuDao.addStock(skuId, wareId, skuNum);
        }
    }

    //查询是否有库存，参数传json
    @Override
    public List<SkuHasStockVo> getSkuHasStock(List<Long> skuIds) {
        /**
         * 库存 = 库存总合 - 锁定库存总和
         * 查每个skuid的库存
         *
         * 循环查SELECT SUM(stock-stock_locked) AS stocksum FROM `wms_ware_sku` WHERE sku_id=?
         * 分组查SELECT sku_id,SUM(stock-stock_locked) AS stocksum FROM `wms_ware_sku` GROUP BY sku_id
         */
        List<SkuHasStockVo> collect = skuIds.stream().map(skuId -> {
            Long count = baseMapper.selectSumStock(skuId);
            SkuHasStockVo skuHasStockVo = new SkuHasStockVo();
            skuHasStockVo.setSkuId(skuId);
            skuHasStockVo.setHasStock(count==null?false:count>0);
            return skuHasStockVo;
        }).collect(Collectors.toList());

        return collect;
    }


    /**
     * 库存锁定
     *
     * 1.查询该商品在那些仓库有库存
     * 2.锁定库存，遍历仓库去锁定库存，只要有一个仓库锁定代表成功，如果没有一个仓库能锁成功，抛异常，该sku商品库存不足
     *
     */
//    @Transactional(rollbackFor = NoWareStockException.class)
    @Transactional
    @Override
    public boolean orderStock(OrderStockRequest orderStockRequest) {
        //保存工作单
        WareOrderTaskEntity wareOrderTaskEntity = new WareOrderTaskEntity();
        wareOrderTaskEntity.setOrderSn(orderStockRequest.getOrderSn());
        wareOrderTaskService.save(wareOrderTaskEntity);

        List<OrderItemVo> itemVos = orderStockRequest.getItemVos();
        List<SkuStockfromWare> collect = itemVos.stream().map(item -> {
            SkuStockfromWare skuStockfromWare = new SkuStockfromWare();
            skuStockfromWare.setSkuId(item.getSkuId());
            skuStockfromWare.setNum(item.getCount());
            //查询该商品在那些仓库有库存
            List<Long> wareId = wareSkuDao.skuStockfromWare(item.getSkuId());
            skuStockfromWare.setWareId(wareId);
            return skuStockfromWare;
        }).collect(Collectors.toList());

        //根据skuId遍历
        for (SkuStockfromWare skuStockfromWare : collect) {
            //判断是否锁定成功
            boolean flag = false;

            //判断该商品是否有仓库存在库存
            List<Long> wareIdList = skuStockfromWare.getWareId();
            if (wareIdList.size() < 0 || wareIdList == null){
                throw new NoWareStockException(skuStockfromWare.getSkuId());
            }
            for (Long wareId : wareIdList) {
                Long count = wareSkuDao.LockedStockFromWare(skuStockfromWare.getSkuId(),wareId,skuStockfromWare.getNum());
                if (count.equals(1L)){
                    //锁定成功
                    flag = true;

                    //保存工作单详情
                    WareOrderTaskDetailEntity wareOrderTaskDetailEntity = new WareOrderTaskDetailEntity();
                    wareOrderTaskDetailEntity.setSkuId(skuStockfromWare.getSkuId());
                    wareOrderTaskDetailEntity.setSkuNum(skuStockfromWare.getNum());
                    wareOrderTaskDetailEntity.setTaskId(wareOrderTaskEntity.getId());
                    wareOrderTaskDetailEntity.setWareId(wareId);
                    //库存状态-已锁定
                    wareOrderTaskDetailEntity.setLockStatus(1);
                    wareOrderTaskDetailService.save(wareOrderTaskDetailEntity);
                    //TODO 库存锁定成功->发消息给mq
                    StockLockedTo stockLockedTo = new StockLockedTo();
                    stockLockedTo.setTaskId(wareOrderTaskEntity.getId());
                    stockLockedTo.setDetailId(wareOrderTaskDetailEntity.getId());
                    //convertAndSend(String exchange, String routingKey, Object object)
                    rabbitTemplate.convertAndSend("stock-event-exchange","stock.locked",stockLockedTo);

                    //该商品锁定库存成功就执行下一个商品
                    break;
                }

            }

            //如果没有一个仓库扣成功，代表此skuId的库存不足
            if (!flag){
                throw new SkuNoStockException(skuStockfromWare.getSkuId());
            }

        }
        return true;
    }


    /**
     * 解锁库存
     *
     * （1）.判断工作单是否存在
     *      不存在代表库存服务已回滚，不做处理
     *      （2）.查询订单是否存在
     *          如果订单不存在，表示订单服务已回滚
     *             （3）.执行解锁库存操作
     *          如果存在，查询订单状态是否为 4-已关闭
     *                如果是 4-已关闭,执行解锁库存操作,订单其他状态不做处理
     */
    @Override
    public void unlockStock(StockLockedTo lockedTo) {

        WareOrderTaskEntity taskEntity = wareOrderTaskService.getById(lockedTo.getTaskId());
        //已回滚不做处理
        if (taskEntity != null){
            //查询订单是否存在
            R<OrderVo> r = orderFeignService.orderStatus(taskEntity.getOrderSn());
            if (r.getCode() == 0){
                OrderVo orderVo = r.getData(new TypeReference<OrderVo>() {
                });
                if (orderVo == null || orderVo.getStatus() == 4){
                    WareOrderTaskDetailEntity detailTo = wareOrderTaskDetailService.getById(lockedTo.getDetailId());
                    //TODO 一定要拿到最新状态
                    //判断工作单的状态是否为 1-已锁定，证明只做了锁定库存操作
                    if (detailTo.getLockStatus().equals(1)){
                        //恢复库存
                        unlock(detailTo.getId(),detailTo.getSkuNum(),detailTo.getSkuId(),detailTo.getWareId());
                    }
                }
            }else {
                throw new OrderFeignException();
            }

        }
    }

    /**
     * 关闭完订单直接解锁库存
     */
    @Override
    public void unlockStock(OrderTo orderTo) {
        WareOrderTaskEntity orderTaskEntity = wareOrderTaskService.getOne(new QueryWrapper<WareOrderTaskEntity>().eq("order_sn", orderTo.getOrderSn()));
        List<WareOrderTaskDetailEntity> list = wareOrderTaskDetailService.getLockStatus(orderTaskEntity);
        for (WareOrderTaskDetailEntity detail : list) {
            System.out.println("解锁库存");
            unlock(detail.getId(),detail.getSkuNum(),detail.getSkuId(),detail.getWareId());
        }

    }

    /**
     * 解锁库存
     * UPDATE `wms_ware_sku` SET stock_locked = stock_locked - ?
     * WHERE sku_id = ? AND ware_id = ?
     */
    private void unlock(Long id,Integer skuNum, Long skuId, Long wareId) {
        wareSkuDao.unlock(skuNum,skuId,wareId);
        //修改状态为 已解锁
        WareOrderTaskDetailEntity wareOrderTaskDetailEntity = new WareOrderTaskDetailEntity();
        wareOrderTaskDetailEntity.setId(id);
        wareOrderTaskDetailEntity.setLockStatus(2);
        wareOrderTaskDetailDao.updateById(wareOrderTaskDetailEntity);
    }








}